﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace CallNumberSystem
{
    public class SocketServerHelper
    {
        private Socket socketWatch;
        private Socket socketClient;
        public bool SocketIsOK = false;
        public Dictionary<string, SocketClientObject> ClientDic = new Dictionary<string, SocketClientObject>();
        public readonly IPAddress ipAddress;
        public readonly int port = 0;
        private readonly Action<string> receiveAction;
        private readonly Action errListenAction;
        private readonly int linstenCount;
        private readonly Action<string> watchClientConnection;
        private readonly Action<string> watchClientClose;

        public SocketServerHelper(IPAddress ipAddress, int port, Action<string> receiveAction = null, Action errListenAction = null, int LinstenCount = 0, Action<string> WatchClientConnection = null, Action<string> WatchClientClose = null)
        {
            this.ipAddress = ipAddress;
            this.port = port;
            this.receiveAction = receiveAction;
            this.errListenAction = errListenAction;
            this.linstenCount = LinstenCount;
            this.watchClientConnection = WatchClientConnection;
            this.watchClientClose = WatchClientClose;
            CreatSocket();
        }

        private Thread listenThread;

        private void CreatSocket()
        {
            try
            {
                //点击开始监听时 在服务端创建一个负责监听IP和端口号的Socket

                socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                //创建对象端口
                IPEndPoint point = new IPEndPoint(ipAddress, port);

                socketWatch.Bind(point);//绑定端口号
                socketWatch.Listen(linstenCount);//设置监听

                if (listenThread == null)
                {
                    //创建监听线程
                    listenThread = new Thread(Listen)
                    {
                        IsBackground = true
                    };
                    listenThread.Start(socketWatch);
                    SocketIsOK = true;
                }
                WriteLogHelper.WriteLog($"创建Socket服务器{ipAddress}:{port}成功", LogType.SocketOK);
            }
            catch (Exception ex)
            {
                WriteLogHelper.WriteLog($"创建Socket服务器{ipAddress}:{port}失败：" + ex.Message, LogType.SocketERROR);
                errListenAction?.Invoke();
            }
        }

        private void Listen(object o)
        {
            try
            {
                while (true)
                {
                    socketClient = socketWatch.Accept();//等待接收客户端连接
                    string clientID = socketClient.RemoteEndPoint.ToString();
                    WriteLogHelper.WriteLog($"客户端{clientID}连接成功：", LogType.SocketOK);
                    if (!ClientDic.ContainsKey(clientID))
                    {
                        ClientDic.Add(clientID, new SocketClientObject { Client = socketClient, ConnectDateTime = DateTime.Now });
                    }
                    //开启一个新线程，执行接收消息方法
                    Thread r_thread = new Thread(Received)
                    {
                        IsBackground = true
                    };
                    watchClientConnection?.Invoke(clientID);
                    r_thread.Start(socketClient);
                    Send("OK:" + clientID);
                }
            }
            catch (SocketException ex)
            {
                SocketIsOK = false;
                WriteLogHelper.WriteLog("服务器监听错误1：" + ex.Message, LogType.SocketERROR);
            }
            catch (ObjectDisposedException ex)
            {
                SocketIsOK = false;

                WriteLogHelper.WriteLog("服务器监听错误2：" + ex.Message, LogType.SocketERROR);
            }
            catch (InvalidOperationException ex)
            {
                SocketIsOK = false;

                WriteLogHelper.WriteLog("服务器监听错误3：" + ex.Message, LogType.SocketERROR);
            }
        }

        private void Received(object o)
        {
            byte[] buffer = new byte[1024 * 1024 * 3];
            int len = 0;
            do
            {
                //客户端连接服务器成功后，服务器接收客户端发送的消息
                //实际接收到的有效字节数
                try
                {
                    string clientID = socketClient.RemoteEndPoint.ToString();
                    if (!socketClient.Connected)
                    {
                        ClientDic.Remove(clientID);
                        WriteLogHelper.WriteLog($"客户端{clientID}已断开：", LogType.SocketOK);
                        break;
                    }
                    len = socketClient.Receive(buffer, buffer.Length, 0);
                    if (len == 0)
                    {
                        ClientDic.Remove(clientID);
                        watchClientClose?.Invoke(clientID);
                        WriteLogHelper.WriteLog($"客户端{clientID}断开连接", LogType.SocketOK);
                        break;
                    }
                    string str = Encoding.UTF8.GetString(buffer, 0, len);
                    //接收到消息执行的方法
                    receiveAction?.Invoke(str);
                    Send("OK");
                    WriteLogHelper.WriteLog($"接收{clientID}消息成功，接收内容：" + str, LogType.SocketOK);
                }
                catch (ArgumentNullException ex)
                {
                    SocketIsOK = false;
                    ClientDic.Remove(socketClient.RemoteEndPoint.ToString());

                    WriteLogHelper.WriteLog($"服务器接收消息错误1：" + ex.Message, LogType.SocketERROR);
                }
                catch (SocketException ex)
                {
                    SocketIsOK = false;
                    ClientDic.Remove(socketClient.RemoteEndPoint.ToString());

                    WriteLogHelper.WriteLog("服务器接收消息错误2：" + ex.Message, LogType.SocketERROR);
                }
                catch (ObjectDisposedException ex)
                {
                    SocketIsOK = false;
                    ClientDic.Remove(socketClient.RemoteEndPoint.ToString());

                    WriteLogHelper.WriteLog("服务器接收消息错误3：" + ex.Message, LogType.SocketERROR);
                }
                catch (System.Security.SecurityException ex)
                {
                    SocketIsOK = false;
                    ClientDic.Remove(socketClient.RemoteEndPoint.ToString());

                    WriteLogHelper.WriteLog("服务器接收消息错误4：" + ex.Message, LogType.SocketERROR);
                }
            } while (len > 0);
        }

        public void Send(string sendMsg)
        {
            byte[] buffer = Encoding.UTF8.GetBytes(sendMsg);
            try
            {
                socketClient.Send(buffer);
                WriteLogHelper.WriteLog($"服务器向{socketClient.RemoteEndPoint}发送消息成功，发送内容：" + Encoding.UTF8.GetString(buffer, 0, buffer.Length), LogType.SocketOK);
            }
            catch (ArgumentNullException ex)
            {
                SocketIsOK = false;
                WriteLogHelper.WriteLog("服务器消息发送错误1：" + ex.Message, LogType.SocketERROR);
            }
            catch (SocketException ex)
            {
                SocketIsOK = false;
                WriteLogHelper.WriteLog("服务器消息发送错误2：" + ex.Message, LogType.SocketERROR);
            }
            catch (ObjectDisposedException ex)
            {
                SocketIsOK = false;
                WriteLogHelper.WriteLog("服务器消息发送错误3：" + ex.Message, LogType.SocketERROR);
            }
        }

        public void Close()
        {
            try
            {
                socketClient.Shutdown(SocketShutdown.Both);
                socketClient.Close();
                WriteLogHelper.WriteLog("服务器正常关闭！", LogType.SocketOK);
            }
            catch
            {
                WriteLogHelper.WriteLog("服务器关闭错误！", LogType.SocketERROR);
            }
        }
    }

    public class SocketClientHelper
    {
        private readonly Socket sockClient;
        private readonly Action<string> recAction;
        public readonly IPAddress ipAddress;
        public readonly int port = 0;
        public bool SocketIsOK = false;

        public SocketClientHelper(IPAddress ipAddress, int port, Action<string> receiveAction, Action errConnectAction)
        {
            this.ipAddress = ipAddress;
            this.port = port;
            try
            {
                //创建客户端Socket，获得远程ip和端口号
                sockClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                IPEndPoint point = new IPEndPoint(ipAddress, port);

                sockClient.Connect(point);
                //开启新的线程，不停的接收服务器发来的消息
                Thread c_thread = new Thread(Received)
                {
                    IsBackground = true
                };
                recAction = receiveAction;
                c_thread.Start();
                SocketIsOK = true;
                WriteLogHelper.WriteLog($"服务器{ipAddress}:{port}连接成功", LogType.SocketOK);
            }
            catch
            {
                errConnectAction?.Invoke();
                WriteLogHelper.WriteLog($"服务器{ipAddress}:{port}连接失败", LogType.SocketERROR);
            }
        }

        private void Received()
        {
            try
            {
                while (true)
                {
                    //客户端连接服务器成功后，服务器接收客户端发送的消息
                    byte[] buffer = new byte[1024 * 1024 * 3];
                    //实际接收到的有效字节数
                    int len = sockClient.Receive(buffer);
                    if (len == 0)
                    {
                        break;
                    }
                    string str = Encoding.UTF8.GetString(buffer, 0, len);
                    //接收到消息执行的方法
                    recAction?.Invoke(str);
                    WriteLogHelper.WriteLog("接收成功，接收内容：" + str, LogType.SocketOK);
                }
            }
            catch (ArgumentNullException ex)
            {
                SocketIsOK = false;
                WriteLogHelper.WriteLog("接收错误1：" + ex.Message, LogType.SocketERROR);
            }
            catch (SocketException ex)
            {
                SocketIsOK = false;
                WriteLogHelper.WriteLog("接收错误2：" + ex.Message, LogType.SocketERROR);
            }
            catch (ObjectDisposedException ex)
            {
                SocketIsOK = false;
                WriteLogHelper.WriteLog("接收错误3：" + ex.Message, LogType.SocketERROR);
            }
            catch (System.Security.SecurityException ex)
            {
                SocketIsOK = false;
                WriteLogHelper.WriteLog("接收错误4：" + ex.Message, LogType.SocketERROR);
            }
        }

        public void Send(string sendMsg)
        {
            byte[] buffer = Encoding.UTF8.GetBytes(sendMsg);
            sockClient.Send(buffer);
            try
            {
                WriteLogHelper.WriteLog($"客户端{sockClient.RemoteEndPoint}消息发送成功，发送内容：" + Encoding.UTF8.GetString(buffer, 0, buffer.Length), LogType.SocketOK);
            }
            catch (ArgumentNullException ex)
            {
                SocketIsOK = false;
                WriteLogHelper.WriteLog($"客户端{sockClient.RemoteEndPoint}消息发送错误1：" + ex.Message, LogType.SocketERROR);
            }
            catch (SocketException ex)
            {
                SocketIsOK = false;
                WriteLogHelper.WriteLog($"客户端{sockClient.RemoteEndPoint}消息发送错误2：" + ex.Message, LogType.SocketERROR);
            }
            catch (ObjectDisposedException ex)
            {
                SocketIsOK = false;
                WriteLogHelper.WriteLog($"客户端{sockClient.RemoteEndPoint}消息发送错误3：" + ex.Message, LogType.SocketERROR);
            }
        }

        public void Close()
        {
            try
            {
                sockClient.Shutdown(SocketShutdown.Both);
                sockClient.Close();
            }
            catch
            {
                WriteLogHelper.WriteLog($"服务器关闭失败！", LogType.SocketERROR);
            }
        }
    }

    public class SocketClientObject
    {
        public Socket Client;
        public DateTime ConnectDateTime;
    }
}